package edu.arizona.sista.learn.activity.util;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import edu.arizona.sista.learn.activity.model.Conjunction;
import edu.arizona.sista.learn.activity.model.Predicate;
import edu.arizona.sista.learn.activity.model.Relation;
import edu.arizona.sista.learn.activity.model.RelationType;
import edu.arizona.sista.learn.activity.model.args.PredActor;
import edu.arizona.sista.learn.activity.model.args.PredAgent;
import edu.arizona.sista.learn.activity.model.args.PredArg;
import edu.arizona.sista.learn.activity.model.args.PredLocation;
import edu.arizona.sista.learn.activity.model.args.PredObject;

@SuppressWarnings("serial")
public class KnownPredicates {
	
//	public static Predicate at;
//	public static Predicate dd;
//	public static Predicate ddts;
//	public static Predicate di;
//	public static Predicate dits;
//	public static Predicate distanceStable;
//	public static Predicate moving;
//	public static Predicate mfh;
//	public static Predicate runGesture;
//	public static Predicate speedDecreasing;
//	public static Predicate speedIncreasing;
//	public static Predicate trajectoryStable;
//	public static Predicate walkGesture;
//	
//	public static Predicate notAt;
//	public static Predicate notMoving;
//	
//	public static Predicate itc;
//	public static Predicate itga;
//	public static Predicate itgv;
//	
//	public static Predicate approach;
//	public static Predicate arrive;
//	public static Predicate chase;
//	public static Predicate flee;
//	public static Predicate follow;
//	public static Predicate get;
//	public static Predicate give;
//	public static Predicate go;
//	public static Predicate hand;
//	public static Predicate have;
//	public static Predicate pass;
//	public static Predicate run;
//	public static Predicate stop;
//	public static Predicate walk;
//	
//	public static Predicate notHave;
	

	public static final String X = "?x";
	public static final String Y = "?y";
	public static final String Z = "?z";
	public static final String not = "!";
	//To specify that one variable cannot equal another, follow
	//   follow the variable name with a list of incompatible variables
	//   separated by bangs ie: ?x!?y!?z
	public static final String XnotY = X+not+Y;
	public static final String YnotX = Y+not+X;
	public static final String XnotZ = X+not+Z;
	public static final String ZnotX = Z+not+X;
	
	public static final String notX = not+X;
	
	public static List<Predicate> allPredicates = new ArrayList<Predicate>();
	
	public static List<Predicate> evaluationVerbs = new ArrayList<Predicate>();

	public static Predicate at = new Predicate( PredicateNames.AT, new PredObject(X,null), new PredLocation(Y,null) ); //Maybe XnotY and YnotX?
	
	public static Predicate notAt = new Predicate( PredicateNames.NOT_AT, new PredObject(X,null), new PredLocation(Y,null) );
	
	public static Predicate dd = new Predicate( PredicateNames.DISTANCE_DECREASING, new PredObject(X, null), new PredLocation(Y, null) );
	
	public static Predicate ddts = new Predicate( PredicateNames.DISTANCE_DECREASING_TO_STATIONARY, new PredObject(X, null), new PredLocation(Y, null) );
	
	//TODO: This should take PredObject, PredLocation but we need to make this an alterable signature ie grab a copy that is agent, object
	public static Predicate dits = new Predicate( PredicateNames.DISTANCE_INCREASING_TO_STATIONARY, new PredObject(X, null), new PredLocation(Y, null) );
	
	public static Predicate mfh = new Predicate( PredicateNames.MOVING_FAST_HUMAN, new PredObject(X, null) );

	public static Predicate runGesture = new Predicate(PredicateNames.RUN_GESTURE, new PredActor(X,null) );
	
	public static Predicate walkGesture = new Predicate(PredicateNames.WALK_GESTURE, new PredActor(X,null) );
	
	public static Predicate trajectoryStable = new Predicate(PredicateNames.TRAJECTORY_STABLE, new PredObject(X,null) );

	public static Predicate hand = new Predicate( PredicateNames.HAND, new PredAgent(X, null), new PredActor(Y, null), new PredObject(Z, null));
	
	public static Predicate moving = new Predicate( PredicateNames.MOVING, new PredObject(X,null) );
	
	public static Predicate notMoving = new Predicate( PredicateNames.NOT_MOVING, new PredObject(X,null) );
	
	public static Predicate have = new Predicate( PredicateNames.HAVE, new PredLocation( X, null), new PredObject( Y, null) );

	public static final PredArg[] notHaveArgs = new PredArg[]{new PredLocation( XnotZ, null ), new PredObject( Y, null )};
	public static final Predicate[] notHaveInferredFrom = new Predicate[]{have.recastCopy( new PredLocation(ZnotX,null), notHaveArgs[1] )};
	public static Predicate notHave = new Predicate( PredicateNames.NOT_HAVE, null, null, (Conjunction)null, notHaveInferredFrom,
			notHaveArgs );
	
	public static PredArg[] itcArgs = new PredArg[]{new PredAgent(X, null), new PredObject(Y, null)};
	public static final Predicate[] itcInferredFrom = new Predicate[]{ddts.copy()};
	public static Predicate itc = new Predicate( PredicateNames.INTENDS_TO_CATCH, null, null, (Conjunction)null, itcInferredFrom,
			 itcArgs);
	
	public static final Predicate[] itgaInferredFrom = new Predicate[]{dits.copy()};
	public static Predicate itga = new Predicate( PredicateNames.INTENDS_TO_GET_AWAY, null, null, (Conjunction)null, itgaInferredFrom, 
			new PredAgent(X, null), new PredLocation(Y, null) );
	
	public static final Predicate[] itgvInferredFrom = new Predicate[]{dits.copy()};
	public static Predicate itgv = new Predicate( PredicateNames.INTENDS_TO_GIVE, null, null, (Conjunction)null, itgvInferredFrom, 
			new PredAgent(X, null), new PredActor(Y, null), new PredObject(Z, null) );
	
	
	
	
	//	APPROACH: approach(agent:a,location:b)
	// { ct : ddts(agent:a,location:b), distance-decreasing(agent:a,location:b) }
	public static final PredArg[] approachArgs = new PredArg[]{new PredObject(XnotY,null),new PredLocation(YnotX,null)};
	public static final Relation[] approachDefinition = new Relation[]{
		new Relation(RelationType.ct, ddts.copy(), dd.copy())
	};
	public static Predicate approach = new Predicate(PredicateNames.APPROACH, null, null, approachDefinition, null, approachArgs);
	
	// CHASE
	public static final PredArg[] chaseArgs = new PredArg[]{new PredAgent(XnotY, null), new PredObject(YnotX, null)};
	public static final Map<PredArg,PredArg> chaseVarSubs = new HashMap<PredArg,PredArg>(){{
		put(new PredLocation(X, null), chaseArgs[0]);
		put(new PredLocation(Y, null), chaseArgs[1]);
	}};
	public static final Predicate[] chaseComposedOf = new Predicate[]{ ddts.recastCopy(chaseVarSubs),
		itc.recastCopy(chaseArgs), dits.recastCopy(chaseArgs[1],chaseArgs[0]), mfh.recastCopy(chaseArgs[0]), 
		mfh.recastCopy(chaseArgs[1])};
	public static final List<Relation> chaseDefinition = new ArrayList<Relation>(){{
			add(new Relation(RelationType.ct, chaseComposedOf[0], chaseComposedOf[1]));
			add(new Relation(RelationType.ct, chaseComposedOf[2], this.get(0)));
			add(new Relation(RelationType.ct, chaseComposedOf[3], this.get(1)));
			add(new Relation(RelationType.ct, chaseComposedOf[4], this.get(2)));
	}};
	public static Predicate chase = new Predicate( PredicateNames.CHASE, null,
			chaseComposedOf, chaseDefinition.toArray(new Relation[0]), null,
			chaseArgs );
	
	// FLEE
	public static final PredArg[] fleeArgs = new PredArg[]{new PredAgent(XnotY, null), new PredLocation(YnotX, null)};
	public static final Predicate[] fleeComposedOf = new Predicate[]{ dits.recastCopy(fleeArgs), itga.recastCopy(fleeArgs), mfh.recastCopy(fleeArgs[0])  };
	public static final List<Relation> fleeDefinition = new ArrayList<Relation>(){{
			add(new Relation(RelationType.ct, fleeComposedOf[0], fleeComposedOf[1]));
			add(new Relation(RelationType.ct, fleeComposedOf[2], this.get(0)));
	}};
	public static Predicate flee = new Predicate( PredicateNames.FLEE, null,
			fleeComposedOf, fleeDefinition.toArray(new Relation[0]), null,
			fleeArgs );
	
	// FOLLOW : follow(actor:a,object:b)
	//	{ ct : ddts(actor:a,object:b), dits(object:b, actor:a), moving(actor:a), moving(object:b), distance-stable(actor:a, object:b) }
	public static final PredArg[] followArgs = new PredArg[]{new PredActor(XnotY,null), new PredObject(YnotX,null)};
	public static final Relation[] followDefinition = new Relation[]{
		new Relation( RelationType.ct, ddts.recastCopy(followArgs), dits.recastCopy(followArgs[1],followArgs[0]) )
	};
	public static final Predicate[] followInferredFrom = new Predicate[]{chase.copy()};
	public static Predicate follow = new Predicate(PredicateNames.FOLLOW, null, null, followDefinition, followInferredFrom, followArgs);
	
	// MOVE : translation move (aka movepos)
	public static final PredArg[] moveposArgs = new PredArg[]{new PredObject(X, null)};
	public static final Predicate[] moveposComposedOf = new Predicate[] { moving.recastCopy(moveposArgs[0]) };
	public static final List<Relation> moveposDefinition = new ArrayList<Relation>(){{
		add(new Relation(RelationType.ct, moveposComposedOf[0], moveposComposedOf[0]));
	}};
	public static Predicate movepos = new Predicate( PredicateNames.MOVEPOS, null, moveposComposedOf,
			moveposDefinition.toArray(new Relation[0]), null, moveposArgs);

	// WALK : walk(agent:a) : agent moving
	public static final PredArg[] walkArgs = new PredArg[]{new PredActor(X, null)};
	public static final Predicate[] walkComposedOf = new Predicate[]{ moving.recastCopy(walkArgs[0]) };
	public static final List<Relation> walkDefinition = new ArrayList<Relation>(){{
		add(new Relation(RelationType.ct, walkComposedOf[0], walkComposedOf[0]));
	}};
	public static Predicate walk = new Predicate( PredicateNames.WALK, null, walkComposedOf,
			walkDefinition.toArray(new Relation[0]), null, walkArgs);
	
	// RUN : run(agent:a) : agent moving fast
	public static final PredArg[] runArgs = new PredArg[]{new PredActor(X, null)};
	public static final Predicate[] runComposedOf = new Predicate[]{ mfh.recastCopy(runArgs[0]) };
	public static final List<Relation> runDefinition = new ArrayList<Relation>(){{
		add(new Relation(RelationType.ct, runComposedOf[0], runComposedOf[0]));
	}};
	public static Predicate run = new Predicate( PredicateNames.RUN, null, runComposedOf,
			runDefinition.toArray(new Relation[0]), null, runArgs);
	
	
	// GO : hacked definition
	// hopefully better hacked definition 8/16 JBW
	public static final Predicate[] goComposedOf = new Predicate[]{ notMoving.copy(), moving.copy() };
	public static final List<Relation> goDefinition = new ArrayList<Relation>(){{
		add(new Relation(RelationType.m_window, goComposedOf[0], goComposedOf[1]));
	}};
	public static Predicate go = new Predicate( PredicateNames.GO, null, goComposedOf,
			goDefinition.toArray(new Relation[0]), null, new PredObject(X, null));
//	public static final Predicate[] goComposedOf = new Predicate[]{moving.copy(), notMoving.copy()};
//	public static final Relation[] goDefinition = new Relation[]{new Relation(RelationType.m, goComposedOf[0], goComposedOf[1])};
//	public static Predicate go = new Predicate( PredicateNames.GO, null, goComposedOf, goDefinition, null, new PredObject(X, null) );
	
	// STOP : hacked definition
	// hopefully better hacked definition 8/16 JBW
	public static final Predicate[] stopComposedOf = new Predicate[]{ moving.copy(), notMoving.copy() };
	public static final List<Relation> stopDefinition = new ArrayList<Relation>(){{
		add(new Relation(RelationType.m_window, stopComposedOf[0], stopComposedOf[1]));
	}};
	public static Predicate stop = new Predicate( PredicateNames.STOP, null, stopComposedOf,
			stopDefinition.toArray(new Relation[0]), null, new PredObject(X, null));
//	public static final Predicate[] stopComposedOf = new Predicate[]{notMoving.copy(), moving.copy()};
//	public static final Relation[] stopDefinition = new Relation[]{new Relation(RelationType.m, stopComposedOf[0], stopComposedOf[1])};
//	public static Predicate stop = new Predicate( PredicateNames.STOP, null, stopComposedOf, stopDefinition, null, new PredObject(X, null) );
	
	
	// PASS : pass(object:a,location:b) - (passing by: agents approach, meet, then move away)
	//	{ meet : approach(object:a, location:b), dits(object:a, location:b) ; 
	//	  ct : approach(object:a, location:b), trajectory-stable(object:a) ; 
	//	  ct : dits(object:a, location:b), trajectory-stable(object:a) }
	public static final PredArg[] passArgs = new PredArg[]{new PredObject(X,null),new PredLocation(Y,null)};
	public static final Predicate[] passComposedOf = new Predicate[]{approach.copy(),trajectoryStable.copy(),dits.copy()};
	public static final List<Relation> passDefinition = new ArrayList<Relation>(){{
		add(new Relation( RelationType.ct, passComposedOf[0], passComposedOf[1] ));
		add(new Relation( RelationType.ct, passComposedOf[2], passComposedOf[1] ));
		add(new Relation( RelationType.m_window, this.get(0), this.get(1) ));
//		add(new Relation( RelationType.pr, this.get(0), this.get(1) ));
//		add(new Relation( RelationType.bd, this.get(0), this.get(1) ));		// return full interval as pass
	}};
	public static Predicate pass = new Predicate(PredicateNames.PASS, null,
			passComposedOf, passDefinition.toArray(new Relation[0]), null, passArgs);
	
	
	
	
	public static final PredArg[] holdArgs = new PredArg[]{new PredActor(X,null), new PredObject(Y,null)};
	public static Predicate hold = new Predicate( PredicateNames.HOLD, new Predicate[]{have.recastCopy(holdArgs)}, 
			null, (Conjunction)null, null, holdArgs);
	
	
	public static final Relation[] getDefinition = new Relation[]{
		new Relation(RelationType.m, notHave.copy(), have.copy()) };
	public static Predicate get = new Predicate( PredicateNames.GET, null, null, getDefinition, null,
			new PredLocation(X, null), new PredObject(Y, null));
	
	
	/* TODO: Make definition more generalized instead of using 'hand' */
	public static final PredArg[] giveArgs = new PredArg[]{new PredAgent(X, null), new PredActor(Y, null), new PredObject(Z, null)};
	public static final Predicate[] giveComposedOf = new Predicate[]{ hand.copy(), itgv.copy(), have.recastCopy(giveArgs[0],giveArgs[1]), 
		get.recastCopy(giveArgs[1],giveArgs[2])};
	public static final List<Relation> giveDefinition = new ArrayList<Relation>(){{
			add(new Relation(RelationType.ct, giveComposedOf[1], giveComposedOf[3]));
			add(new Relation(RelationType.ct, giveComposedOf[1], giveComposedOf[0]));
			add(new Relation(RelationType.oh, giveComposedOf[2], giveComposedOf[0]));
			add(new Relation(RelationType.st, giveComposedOf[2], giveComposedOf[0]));
			add(new Relation(RelationType.ol, giveComposedOf[0], giveComposedOf[3]));
	}};
	public static Predicate give = new Predicate( PredicateNames.GIVE, null,
			giveComposedOf, giveDefinition.toArray(new Relation[0]), null,
			giveArgs );

	public static final Predicate[] arriveComposedOf = new Predicate[]{stop.copy(), at.copy()};
	//Definition seen in videos
	public static final Relation[] arriveDefinition = new Relation[]{new Relation(RelationType.ct, arriveComposedOf[0], arriveComposedOf[1])};
	//How I would define it -jbw
//	public static final Relation[] arriveDefintion = new Relation[]{new Relation(RelationType.m, notAt.copy(), at.copy())};
	public static Predicate arrive = new Predicate(PredicateNames.ARRIVE, null, arriveComposedOf, arriveDefinition, null, new PredObject(X,null), new PredLocation(Y,null));
	
	
	

	public static Map<String, List<Predicate>> extendedTo;
	
	static {
		
		{ evaluationVerbs.add(approach); }
		{ evaluationVerbs.add(chase); }
		{ evaluationVerbs.add(flee); }
		{ evaluationVerbs.add(follow); }
		{ evaluationVerbs.add(movepos); }
		{ evaluationVerbs.add(walk); }
		{ evaluationVerbs.add(run); }
		{ evaluationVerbs.add(go); }
		{ evaluationVerbs.add(stop); }
		{ evaluationVerbs.add(pass); }
		
		{ evaluationVerbs.add(hand); }
		{ evaluationVerbs.add(hold); }
		{ evaluationVerbs.add(have); }
		{ evaluationVerbs.add(get); }
		
//		{ evaluationVerbs.add(arrive); }
//		{ evaluationVerbs.add(give); }
		
		
		{ allPredicates.add(at); }
		{ allPredicates.add(notAt); }
		{ allPredicates.add(dd); }
		{ allPredicates.add(ddts); }
		{ allPredicates.add(dits); }
		{ allPredicates.add(mfh); }
		{ allPredicates.add(moving); }
		{ allPredicates.add(notMoving); }
		{ allPredicates.add(runGesture); }
		{ allPredicates.add(trajectoryStable); }
		{ allPredicates.add(walkGesture); }
		{ allPredicates.add(itc); }
		{ allPredicates.add(itga); }
		{ allPredicates.add(itgv); }
		{ allPredicates.add(approach); }
		{ allPredicates.add(chase); }
		{ allPredicates.add(flee); }
		{ allPredicates.add(follow); }
		{ allPredicates.add(go); }
		{ allPredicates.add(hand); }
		{ allPredicates.add(have); }
		{ allPredicates.add(hold); }
		{ allPredicates.add(pass); }
		{ allPredicates.add(run); }
		{ allPredicates.add(stop); }
		{ allPredicates.add(walk); }
		{ allPredicates.add(arrive); }
		{ allPredicates.add(notHave); }
		{ allPredicates.add(get); }
		{ allPredicates.add(give); }
		
		extendedTo = 
			new HashMap<String, List<Predicate>>(){
			{
				for( Predicate predicate : allPredicates){
					
					if( predicate.getSupers() != null )
						for( Predicate superPredicate : predicate.getSupers() ){
							if( !containsKey(superPredicate.getName()) )
								put( superPredicate.getName(), new ArrayList<Predicate>() );
							get(superPredicate.getName()).add(predicate);
						}
							
				}
			}
		};
		
//		System.out.println(extendedTo);
		
	}
	/*
	 * The plan is to load these predicates from XML or text
	 * and compile this list as you go
	 */
	// = 
//		new HashMap<String, List<Predicate>>(){
//		{
//			for( Predicate predicate : allPredicates){
//				
//				if( predicate.getSupers() != null )
//					for( Predicate superPredicate : predicate.getSupers() ){
//						if( !containsKey(superPredicate) )
//							put( superPredicate.getName(), new ArrayList<Predicate>() );
//						get(superPredicate).add(predicate);
//					}
//						
//			}
//		}
//	};
}
	